package org.xx.armory.spring5;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import static org.apache.commons.lang3.StringUtils.isBlank;

public class DateConverter {
    private final ZoneId zone;

    private final Date minDate;

    private final Date maxDate;

    public DateConverter() {
        this(null);
    }

    public DateConverter(
            String offset
    ) {
        this.zone = !isBlank(offset) ? ZoneOffset.of(offset) : ZoneId.systemDefault();
        this.minDate = Date.from(LocalDate.of(1970, Month.JANUARY, 1).atStartOfDay().atZone(this.zone).toInstant());
        this.maxDate = Date.from(LocalDate.of(4000, Month.JANUARY, 1).atStartOfDay().atZone(this.zone).toInstant());
    }

    public Date buildDate(
            LocalDate localDate
    ) {
        return localDate != null ? Date.from(localDate.atStartOfDay().atZone(this.zone).toInstant()) : null;
    }

    public Date buildDate(
            LocalDateTime localDateTime
    ) {
        return localDateTime != null ? Date.from(localDateTime.atZone(zone).toInstant()) : null;
    }

    public LocalDate buildLocalDate(
            Date date
    ) {
        return date != null ? LocalDate.ofInstant(date.toInstant(), zone) : null;
    }

    public LocalDateTime buildLocalDateTime(
            Date date
    ) {
        return date != null ? LocalDateTime.ofInstant(date.toInstant(), zone) : null;
    }

    public DateTimeFormatter buildFormatter(
            String pattern
    ) {
        return DateTimeFormatter.ofPattern(pattern).withZone(this.zone);
    }

    /**
     * 将指定的日期截断到该日的开头。
     *
     * @param d
     *         指定日期。
     * @return 截断的结果，如果参数{@code d} 是{@code null}，那么返回逻辑最小日期。
     * @see #buildDate(LocalDate)
     * @see #buildLocalDate(Date)
     */
    public Date truncate(
            Date d
    ) {
        return d != null ? buildDate(buildLocalDate(d)) : this.minDate;
    }

    /**
     * 将指定的日期提升到该日的末尾。
     *
     * @param d
     *         指定日期。
     * @return 提升的结果，如果参数{@code d} 是{@code null}，那么返回逻辑最大日期。
     * @see #buildDate(LocalDate)
     * @see #buildLocalDate(Date)
     */
    public Date ceiling(
            Date d
    ) {
        return d != null ? buildDate(buildLocalDate(d).plusDays(1)) : this.maxDate;
    }

    /**
     * 将指定的日期截断到该月的开头。
     *
     * @param d
     *         指定日期。
     * @return 截断的结果，如果参数{@code d} 是{@code null}，那么返回逻辑最小日期。
     * @see #buildDate(LocalDate)
     * @see #buildLocalDate(Date)
     */
    public Date truncateMonth(
            Date d
    ) {
        return d != null ? buildDate(buildLocalDate(d).withDayOfMonth(1)) : this.minDate;
    }

    /**
     * 将指定的日期提升到该月的末尾。
     *
     * @param d
     *         指定日期。
     * @return 提升的结果，如果参数{@code d} 是{@code null}，那么返回逻辑最大日期。
     * @see #buildDate(LocalDate)
     * @see #buildLocalDate(Date)
     */
    public Date ceilingMonth(
            Date d
    ) {
        return d != null ? buildDate(buildLocalDate(d).plusMonths(1).withDayOfMonth(1)) : this.maxDate;
    }

    /**
     * 将指定的日期截断到该年的开头。
     *
     * @param d
     *         指定日期。
     * @return 截断的结果，如果参数{@code d} 是{@code null}，那么返回逻辑最小日期。
     * @see #buildDate(LocalDate)
     * @see #buildLocalDate(Date)
     */
    public Date truncateYear(
            Date d
    ) {
        return d != null ? buildDate(buildLocalDate(d).withDayOfYear(1)) : this.minDate;
    }

    /**
     * 将指定的日期提升到该年的末尾。
     *
     * @param d
     *         指定日期。
     * @return 提升的结果，如果参数{@code d} 是{@code null}，那么返回逻辑最大日期。
     * @see #buildDate(LocalDate)
     * @see #buildLocalDate(Date)
     */
    public Date ceilingYear(
            Date d
    ) {
        return d != null ? buildDate(buildLocalDate(d).plusYears(1).withDayOfYear(1)) : this.maxDate;
    }
}
